package net.oschina.bilbodai.common.beanutil.bean.codedump;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import static org.objectweb.asm.Opcodes.*;

/**
 * @author daimao  Date: 16/5/27 Time: 下午4:20
 * @version $Id$
 */
public class PropertyDump {

    public void list2list(MethodVisitor mv, ClassVisitor cw) {
        mv = cw.visitMethod(ACC_PUBLIC, "propertyDump", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 1);
        mv.visitTypeInsn(CHECKCAST, "net/oschina/bilbodai/common/beanutil/bean/codedump/CollectionIterCopy$Foo");
        mv.visitVarInsn(ASTORE, 3);
        mv.visitVarInsn(ALOAD, 2);
        mv.visitTypeInsn(CHECKCAST, "net/oschina/bilbodai/common/beanutil/bean/codedump/CollectionIterCopy$FooTarget");
        mv.visitVarInsn(ASTORE, 4);
        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/codedump/CollectionIterCopy$Foo", "getFoos", "()Ljava/util/List;", false);
        Label l0 = new Label();
        mv.visitJumpInsn(IFNULL, l0);


        mv.visitVarInsn(ALOAD, 4);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/codedump/CollectionIterCopy$FooTarget", "getFooTargets", "()Ljava/util/List;", false);
        Label l1 = new Label();
        mv.visitJumpInsn(IFNONNULL, l1);

        mv.visitVarInsn(ALOAD, 4);
        mv.visitTypeInsn(NEW, "java/util/LinkedList");
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "java/util/LinkedList", "<init>", "()V", false);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/codedump/CollectionIterCopy$FooTarget", "setFooTargets", "(Ljava/util/List;)V", false);

        mv.visitLabel(l1);
        mv.visitLdcInsn(Type.getType("Lnet/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo;"));
        mv.visitLdcInsn(Type.getType("Lnet/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo;"));
        mv.visitMethodInsn(INVOKESTATIC, "net/oschina/bilbodai/common/beanutil/bean/BeanDumpUtil", "create", "(Ljava/lang/Class;Ljava/lang/Class;)Lnet/oschina/bilbodai/common/beanutil/bean/IBeanAccessor;", false);
        mv.visitVarInsn(ASTORE, 5);

        mv.visitVarInsn(ALOAD, 4);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/codedump/CollectionIterCopy$FooTarget", "getFooTargets", "()Ljava/util/List;", false);
        mv.visitVarInsn(ASTORE, 6);

        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/codedump/CollectionIterCopy$Foo", "getFoos", "()Ljava/util/List;", false);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;", true);
        mv.visitVarInsn(ASTORE, 7);

        Label l2 = new Label();
        mv.visitLabel(l2);
        mv.visitVarInsn(ALOAD, 7);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true);
        mv.visitJumpInsn(IFEQ, l0);

        mv.visitVarInsn(ALOAD, 7);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true);
        mv.visitTypeInsn(CHECKCAST, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo");
        mv.visitVarInsn(ASTORE, 8);
        mv.visitVarInsn(ALOAD, 6);
        mv.visitVarInsn(ALOAD, 5);
        mv.visitVarInsn(ALOAD, 8);
        mv.visitInsn(ACONST_NULL);
        mv.visitMethodInsn(INVOKEINTERFACE, "net/oschina/bilbodai/common/beanutil/bean/IBeanAccessor", "propertyDump", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true);
        mv.visitInsn(POP);
        mv.visitJumpInsn(GOTO, l2);


        mv.visitLabel(l0);
        mv.visitFrame(Opcodes.F_CHOP,3, null, 0, null);
        mv.visitVarInsn(ALOAD, 4);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(4, 9);
        mv.visitEnd();
    }

    public void dump(MethodVisitor mv, ClassVisitor cw) {
        mv = cw.visitMethod(ACC_PUBLIC, "propertyDump", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", null, null);
        mv.visitCode();

        Label l0 = new Label();
        Label l1 = new Label();
        Label l2 = new Label();

        mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");

        mv.visitLabel(l0); //try

        mv.visitVarInsn(ALOAD, 1);
        mv.visitTypeInsn(CHECKCAST, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo");
        mv.visitVarInsn(ASTORE, 3);

        mv.visitVarInsn(ALOAD, 2);
        Label l3 = new Label();
        mv.visitJumpInsn(IFNONNULL, l3); // new FooTarget
        mv.visitTypeInsn(NEW, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget");
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget", "<init>", "()V", false);
        Label l4 = new Label();
        mv.visitJumpInsn(GOTO, l4);

        mv.visitLabel(l3); // cast target
        mv.visitFrame(Opcodes.F_APPEND,1, new Object[] {"net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo"}, 0, null);
        mv.visitVarInsn(ALOAD, 2);
        mv.visitTypeInsn(CHECKCAST, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget");
        mv.visitLabel(l4);
        mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget"});
        mv.visitVarInsn(ASTORE, 4);

        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo", "getName", "()Ljava/lang/String;", false);
        Label l5 = new Label();
        mv.visitJumpInsn(IFNULL, l5);
        mv.visitVarInsn(ALOAD, 4);
        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo", "getName", "()Ljava/lang/String;", false);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget", "setName", "(Ljava/lang/String;)V", false);
        mv.visitLabel(l5);
        mv.visitFrame(Opcodes.F_APPEND,1, new Object[] {"net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget"}, 0, null);

        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo", "getDate", "()Ljava/util/Date;", false);
        Label l6 = new Label();
        mv.visitJumpInsn(IFNULL, l6);
        mv.visitVarInsn(ALOAD, 4);
        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo", "getDate", "()Ljava/util/Date;", false);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget", "setTime", "(Ljava/util/Date;)V", false);
        mv.visitLabel(l6);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);

        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo", "getFooList", "()Ljava/util/List;", false);
        Label l7 = new Label();
        mv.visitJumpInsn(IFNULL, l7);
        mv.visitLdcInsn(Type.getType("Lnet/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo;"));
        mv.visitLdcInsn(Type.getType("Lnet/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget;"));
        mv.visitMethodInsn(INVOKESTATIC, "net/oschina/bilbodai/common/beanutil/bean/BeanDumpUtil", "create", "(Ljava/lang/Class;Ljava/lang/Class;)Lnet/oschina/bilbodai/common/beanutil/bean/IBeanAccessor;", false);
        mv.visitVarInsn(ASTORE, 5);
        mv.visitVarInsn(ALOAD, 4);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget", "getFooTargets", "()Ljava/util/List;", false);
        mv.visitJumpInsn(IFNONNULL, l7);
        mv.visitLdcInsn(Type.getType("Ljava/util/LinkedList;"));
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "newInstance", "()Ljava/lang/Object;", false);
        mv.visitTypeInsn(CHECKCAST, "java/util/LinkedList");
        mv.visitVarInsn(ASTORE, 6);
        mv.visitVarInsn(ALOAD, 4);
        mv.visitVarInsn(ALOAD, 6);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$FooTarget", "setFooTargets", "(Ljava/util/List;)V", false);
        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo", "getFooList", "()Ljava/util/List;", false);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;", true);
        mv.visitVarInsn(ASTORE, 7);
        Label l8 = new Label();
        mv.visitLabel(l8);
        mv.visitFrame(Opcodes.F_APPEND,3, new Object[] {"net/oschina/bilbodai/common/beanutil/bean/IBeanAccessor", "java/util/LinkedList", "java/util/Iterator"}, 0, null);
        mv.visitVarInsn(ALOAD, 7);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true);
        mv.visitJumpInsn(IFEQ, l7);
        mv.visitVarInsn(ALOAD, 7);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true);
        mv.visitTypeInsn(CHECKCAST, "net/oschina/bilbodai/common/beanutil/bean/DumpBeanSample$Foo");
        mv.visitVarInsn(ASTORE, 8);
        mv.visitVarInsn(ALOAD, 6);
        mv.visitVarInsn(ALOAD, 5);
        mv.visitVarInsn(ALOAD, 8);
        mv.visitInsn(ACONST_NULL);
        mv.visitMethodInsn(INVOKEINTERFACE, "net/oschina/bilbodai/common/beanutil/bean/IBeanAccessor", "propertyDump", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/LinkedList", "add", "(Ljava/lang/Object;)Z", false);
        mv.visitInsn(POP);
        mv.visitJumpInsn(GOTO, l8);
        mv.visitLabel(l7);
        mv.visitFrame(Opcodes.F_CHOP,3, null, 0, null);

        mv.visitVarInsn(ALOAD, 4);
        mv.visitLabel(l1); // } catch
        mv.visitInsn(ARETURN);

        mv.visitLabel(l2); //catch(Exception e) {throw new XX}
        mv.visitFrame(Opcodes.F_FULL, 3, new Object[] {"net/oschina/bilbodai/common/beanutil/bean/CollectionIterCopy", "java/lang/Object", "java/lang/Object"}, 1, new Object[] {"java/lang/Exception"});
        mv.visitVarInsn(ASTORE, 3);
        mv.visitTypeInsn(NEW, "net/oschina/bilbodai/common/beanutil/exception/PropertyDumpException");
        mv.visitInsn(DUP);
        mv.visitLdcInsn("PropertyDump Error");
        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKESPECIAL, "net/oschina/bilbodai/common/beanutil/exception/PropertyDumpException", "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V", false);
        mv.visitInsn(ATHROW);

        mv.visitMaxs(4, 9);
        mv.visitEnd();

    }
}